/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2009 Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    Sam Lantinga
    slouken@libsdl.org
*/

#include "../SDL_cellvideo.h"
#include "../../../events/SDL_events_c.h"
#include <cell/keyboard.h>

/* Data */
#define PS3_MAX_MODS			8
static const SDLKey 			ModifierMap[PS3_MAX_MODS] = {SDLK_LCTRL, SDLK_LSHIFT, SDLK_LALT, SDLK_LSUPER, SDLK_RCTRL, SDLK_RSHIFT, SDLK_RALT, SDLK_RSUPER};//Map PS3 modifier keys to SDL modifier keys

/* Helpers */
static void						CheckKeyboard						()
{
	if(60 < CELL_Video.Keyboard.Check ++)
	{
		CellKbInfo KeyboardInfo;
		cellKbGetInfo(&KeyboardInfo);

		if(!CELL_Video.Keyboard.Have && (KeyboardInfo.status[0] == CELL_KB_STATUS_CONNECTED))
		{
			CELL_Video.Keyboard.Have = 1;

			cellKbSetReadMode(0, CELL_KB_RMODE_PACKET);
			cellKbSetCodeType(0, CELL_KB_CODETYPE_RAW);
			cellKbClearBuf(0);

			CELL_Video.Keyboard.Modifiers = 0;
			memset(CELL_Video.Keyboard.KeyState, 0, sizeof(CELL_Video.Keyboard.KeyState));
		}
		else if(CELL_Video.Keyboard.Have && KeyboardInfo.status[0] != CELL_KB_STATUS_CONNECTED)
		{
			CELL_Video.Keyboard.Have = 0;
			SDL_ResetKeyboard();
		}

		CELL_Video.Keyboard.Check = 0;
	}
}

static void						ProcessModifiers					(uint32_t aNewMods, uint32_t aOldMods)
{
	if(aNewMods != aOldMods)
	{
		for(int i = 0; i != PS3_MAX_MODS; i ++)
		{
			if((aNewMods & (1 << i)) != (aOldMods & (1 << i)))
			{
				CELL_DoModifierEvent(ModifierMap[i], (aNewMods & (1 << i)) ? SDL_PRESSED : SDL_RELEASED);
			}
		}
	}
}

static void						ProcessKeys							(uint32_t aLength, uint16_t* aData)
{
	/* Only process if the list has data, and the list isn't rollover (too many keys pressed at once) */
	if(aLength && aData[0] != 1)
	{
		uint8_t curstate[PS3_MAX_KEYS];
		memset(curstate, 0, PS3_MAX_KEYS);

		//Get list of keys down, aData[0] == 0 means all keys are released
		if(aData[0] > 1)
		{
			for(int i = 0; i != aLength; i ++)
			{
				if(aData[i] < PS3_MAX_KEYS)
				{
					curstate[aData[i]] = aData[i];
				}
			}
		}

		//Send pressed, all downs first, then all ups
		for(int i = 0; i != PS3_MAX_KEYS; i ++)
		{
			if(curstate[i] != CELL_Video.Keyboard.KeyState[i] && curstate[i] != 0)
			{
				CELL_DoKeyEvent((enum HID_ID)i, SDL_PRESSED, CELL_Video.Keyboard.Modifiers, CELL_Video.Keyboard.Leds);
			}
		}

		for(int i = 0; i != PS3_MAX_KEYS; i ++)
		{
			if(curstate[i] != CELL_Video.Keyboard.KeyState[i] && curstate[i] == 0)
			{
				CELL_DoKeyEvent((enum HID_ID)i, SDL_RELEASED, CELL_Video.Keyboard.Modifiers, CELL_Video.Keyboard.Leds);
			}
		}

		memcpy(CELL_Video.Keyboard.KeyState, curstate, PS3_MAX_KEYS);
	}
}

/* Implementation */
void							CELL_KEYBOARD_Process				()
{
	CheckKeyboard();

	CellKbData data;
	if(CELL_Video.Keyboard.Have && CELL_OK == cellKbRead(0, &data) && data.len)
	{
		ProcessModifiers(data.mkey, CELL_Video.Keyboard.Modifiers);
		CELL_Video.Keyboard.Modifiers = data.mkey;
		CELL_Video.Keyboard.Leds = data.led;
		ProcessKeys(data.len, data.keycode);
	}
}

